home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / DT_IN.PY < prev    next >
Encoding:
Text File  |  2000-05-25  |  28.1 KB  |  772 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64. '''Sequence insertion
  65.  
  66.        A sequence may be inserted using an 'in' command.  The 'in'
  67.        command specifies the name of a sequence object and text to
  68.        be inserted for each element in the sequence.  
  69.  
  70.        The EPFS syntax for the in command is::
  71.  
  72.           %(in name)[
  73.                text
  74.           %(in name)]
  75.  
  76.        The HTML syntax for the in command is::
  77.  
  78.           <!--#in name-->
  79.                text 
  80.           <!--#/in name-->
  81.  
  82.       See the example below that shows how 'if', 'else', and 'in' commands
  83.       may be combined to display a possibly empty list of objects.
  84.  
  85.       The text included within an 'in' command will be refered to
  86.       as an 'in' block.
  87.  
  88.     Synopsis
  89.  
  90.       If the variable 'sequence' exists as a sequence, a simple case
  91.       of the 'in' tag is used as follows::
  92.  
  93.          <!--#in sequence-->some markup<!--#/in-->
  94.  
  95.       A more complete case is used as follows::
  96.  
  97.         <!--#in sequence sort=age-->
  98.           <!--#var sequence-number-->) <!--#var age-->
  99.         <!--#/in-->
  100.  
  101.     Attributes
  102.  
  103.       sort -- Define the sort order for sequence items.  If an item in
  104.       the sequence does not define 
  105.  
  106.       reverse -- Reverse the sequence (may be combined with sort).  Note
  107.       that this can cause a huge memory use in lazy activation instances. 
  108.  
  109.       Within an 'in' block, variables are substituted from the
  110.       elements of the iteration.  The elements may be either
  111.       instance or mapping objects.  In addition, the variables:
  112.  
  113.          'sequence-item' -- The element.
  114.  
  115.          'sequence-var-nnn' -- The value of a specific named attribute
  116.            of the item, where 'nnn' is the name.  For example, to get
  117.            an items 'title' attribute, use 'sequence-var-title'.  This
  118.            construct is most useful in an 'if' tag to test whether an
  119.            attribute is present, because the attribute lookup will be
  120.            extended to the full document template namespace.
  121.  
  122.          'sequence-key' -- The key associated with the element in an
  123.            items list. See below.
  124.  
  125.          'sequence-index' -- The index, starting from 0, of the
  126.            element within the sequence.
  127.  
  128.          'sequence-number' -- The index, starting from 1, of the
  129.            element within the sequence.
  130.  
  131.          'sequence-letter' -- The index, starting from 'a', of the
  132.            element within the sequence.
  133.  
  134.          'sequence-Letter' -- The index, starting from 'A', of the
  135.            element within the sequence.
  136.  
  137.          'sequence-roman' -- The index, starting from 'i', of the
  138.            element within the sequence.
  139.  
  140.          'sequence-Roman' -- The index, starting from 'I', of the
  141.            element within the sequence.
  142.  
  143.          'sequence-start' -- A variable that is true if the element
  144.            being displayed is the first of the displayed elements,
  145.            and false otherwise.
  146.  
  147.          'sequence-end' -- A variable that is true if the element
  148.            being displayed is the last of the displayed elements,
  149.            and false otherwise.
  150.  
  151.       are defined for each element.
  152.  
  153.       Normally, 'in' blocks are used to iterate over sequences of
  154.       instances.  If the optional parameter 'mapping' is specified
  155.       after the sequence name, then the elements of the sequence
  156.       will be treated as mapping objects.
  157.  
  158.       An 'in' command may be used to iterate over a sequence of
  159.       dictionary items.  If the elements of the iteration are
  160.       two-element tuples, then then the template code given in the
  161.       'in' block will be applied to the second element of each
  162.       tuple and may use a variable, 'sequence-key' to access the
  163.       first element in each tuple.
  164.  
  165.     Batch sequence insertion
  166.  
  167.       When displaying a large number of objects, it is sometimes
  168.       desireable to display just a sub-sequence of the data.
  169.       An 'in' command may have optional parameters,
  170.       as in::
  171.  
  172.           <!--#in values start=start_var size=7-->
  173.  
  174.       The parameter values may be either integer literals or
  175.       variable names.
  176.  
  177.       Up to five parameters may be set:
  178.  
  179.           'start'   -- The number of the first element to be shown,
  180.                        where elements are numbered from 1.
  181.  
  182.           'end'     -- The number of the last element to be shown,
  183.                        where elements are numbered from 1.
  184.  
  185.           'size'    -- The desired number of elements to be shown at
  186.                        once.
  187.  
  188.           'orphan'  -- The desired minimum number of objects to be
  189.                        displayed.  The default value for this
  190.                        parameter is 3.
  191.  
  192.           'overlap' -- The desired overlap between batches. The
  193.                        default is no overlap.     
  194.  
  195.       Typically, only 'start' and 'size' will be specified.
  196.  
  197.       When batch insertion is used, several additional variables are
  198.       defined for use within the sequence insertion text:
  199.  
  200.           'sequence-query' -- The original query string given in a get
  201.              request with the form variable named in the 'start'
  202.              attribute removed.  This is extremely useful when
  203.              building URLs to fetch another batch.
  204.  
  205.              To see how this is used, consider the following example::
  206.  
  207.                  <!--#in search_results size=20 start=batch_start-->
  208.  
  209.                     ... display rows
  210.  
  211.                     <!--#if sequence-end--> <!--#if next-sequence-->
  212.                       <a href="<!--#var URL-->/<!--#var sequence-query
  213.                           -->&batch_start=<!--#var
  214.                           next-sequence-start-number-->">
  215.                       (Next <!--#var next-sequence-size--> results)
  216.                       </a>
  217.                     <!--#/if--> <!--#/if-->
  218.  
  219.                  <!--#/in-->
  220.  
  221.              If the original URL is: 'foo/bar?x=1&y=2', then the
  222.              rendered text (after row data are displated) will be::
  223.  
  224.                       <a href="foo/bar?x=1&y=2&batch_start=20">
  225.                       (Next 20 results)
  226.                       </a>
  227.  
  228.              If the original URL is: 'foo/bar?batch_start=10&x=1&y=2',
  229.              then the rendered text (after row data are displated)
  230.              will be::
  231.  
  232.                       <a href="foo/bar?x=1&y=2&batch_start=30">
  233.                       (Next 20 results)
  234.                       </a>
  235.  
  236.           'sequence-step-start-index' -- The index, starting from 0,
  237.              of the start of the current batch.
  238.  
  239.           'sequence-step-end-index' -- The index, starting from 0, of
  240.              the end of the current batch.
  241.  
  242.           'sequence-step-size' -- The batch size used.
  243.  
  244.           'previous-sequence' -- This variable will be true when the
  245.              first element is displayed and when the first element
  246.              displayed is not the first element in the sequence.
  247.  
  248.           'previous-sequence-start-index' -- The index, starting from
  249.              0, of the start of the batch previous to the current
  250.              batch.
  251.  
  252.           'previous-sequence-end-index' -- The index, starting from
  253.              0, of the end of the batch previous to the current
  254.              batch.
  255.  
  256.           'previous-sequence-size' -- The size of the batch previous to
  257.              the current batch.
  258.  
  259.           'previous-batches' -- A sequence of mapping objects
  260.              containing information about all of the batches prior
  261.              to the batch being displayed.
  262.  
  263.              Each of these mapping objects include the following
  264.              variables:
  265.  
  266.                 batch-start-index -- The index, starting from
  267.                    0, of the beginning of the batch.
  268.  
  269.                 batch-end-index -- The index, starting from
  270.                    0, of the end of the batch.
  271.  
  272.                 batch-size -- The size of the batch.
  273.  
  274.           'next-sequence' -- This variable will be true when the last
  275.              element is displayed and when the last element
  276.              displayed is not the last element in the sequence.
  277.  
  278.           'next-sequence-start-index' -- The index, starting from
  279.              0, of the start of the batch after the current
  280.              batch.
  281.  
  282.           'next-sequence-end-index' -- The index, starting from
  283.              0, of the end of the batch after the current
  284.              batch.
  285.  
  286.           'next-sequence-size' -- The size of the batch after
  287.              the current batch.
  288.  
  289.           'next-batches' -- A sequence of mapping objects
  290.              containing information about all of the batches after
  291.              the batch being displayed.
  292.  
  293.              Each of these mapping objects include the following
  294.              variables:
  295.  
  296.                 batch-start-index -- The index, starting from
  297.                    0, of the beginning of the batch.
  298.  
  299.                 batch-end-index -- The index, starting from
  300.                    0, of the end of the batch.
  301.  
  302.                 batch-size -- The size of the batch.
  303.  
  304.       For each of the variables listed above with names ending in
  305.       "-index", there are variables with names ending in "-number",
  306.       "-roman", "-Roman", "-letter", and "-Letter" that are indexed
  307.       from 1, "i", "I", "a", and "A", respectively.  In addition,
  308.       for every one of these variables there are variables with
  309.       names ending in "-var-xxx", where "xxx" is an element
  310.       attribute name or key.
  311.  
  312.     Summary statistics
  313.  
  314.       When performing sequence insertion, special variables may be
  315.       used to obtain summary statistics.  To obtain a summary
  316.       statistic for a variable, use the variable name:
  317.       'statistic-name', where 'statistic' is a statistic name and
  318.       'name' is the name of a data variable.
  319.  
  320.       Currently supported statistic names are:
  321.  
  322.         total -- The total of numeric values.
  323.  
  324.         count -- The total number of non-missing values.
  325.  
  326.         min -- The minimum of non-missing values.
  327.  
  328.         max -- The maximum of non-missing values.
  329.  
  330.         median -- The median of non-missing values.
  331.  
  332.         mean -- The mean of numeric values values.
  333.  
  334.         variance -- The variance of numeric values computed with a
  335.           degrees of freedom qeual to the count - 1.
  336.  
  337.         variance-n -- The variance of numeric values computed with a
  338.           degrees of freedom qeual to the count.
  339.  
  340.         standard-deviation -- The standard deviation of numeric values
  341.           computed with a degrees of freedom qeual to the count - 1.
  342.  
  343.         standard-deviation-n -- The standard deviation of numeric
  344.           values computed with a degrees of freedom qeual to the count.
  345.  
  346.       Missing values are either 'None' or the attribute 'Value'
  347.       of the module 'Missing', if present.
  348.  
  349.     'else' continuation tag within in
  350.  
  351.       An 'else' tag may be used as a continuation tag in the 'in' tag.
  352.       The source after the 'else' tag is inserted if:
  353.  
  354.         - The sequence given to the 'in' tag is of zero length, or
  355.  
  356.         - The 'previous' attribute was used and their are no
  357.           previous batches, or
  358.  
  359.         - The 'next' attribute was used and their are no
  360.           next batches, or
  361.  
  362. ''' #'
  363.  
  364. __rcs_id__='$Id: DT_In.py,v 1.40 2000/05/25 16:33:44 shane Exp $'
  365. __version__='$Revision: 1.40 $'[11:-2]
  366.  
  367. from DT_Util import ParseError, parse_params, name_param, str
  368. from DT_Util import render_blocks, InstanceDict, ValidationError
  369. from string import find, atoi, join, split
  370. import ts_regex
  371. from DT_InSV import sequence_variables, opt
  372. TupleType=type(())
  373.  
  374. class InFactory:
  375.     blockContinuations=('else',)
  376.     name='in'
  377.  
  378.     def __call__(self, blocks):
  379.         i=InClass(blocks)
  380.         if i.batch: return i.renderwb
  381.         else: return i.renderwob
  382.  
  383. In=InFactory()
  384.  
  385. class InClass:
  386.     elses=None
  387.     expr=sort=batch=mapping=None
  388.     start_name_re=None
  389.     reverse=None
  390.     
  391.     def __init__(self, blocks):
  392.         tname, args, section = blocks[0]
  393.         args=parse_params(args, name='', start='1',end='-1',size='10',
  394.                           orphan='3',overlap='1',mapping=1,
  395.                           skip_unauthorized=1,
  396.                           previous=1, next=1, expr='', sort='',
  397.                           reverse=1)
  398.         self.args=args
  399.         has_key=args.has_key
  400.  
  401.         if has_key('sort'):
  402.             self.sort=sort=args['sort']
  403.             if sort=='sequence-item': self.sort=''
  404.  
  405.         if has_key('reverse'):
  406.             self.reverse=args['reverse']
  407.             
  408.         if has_key('mapping'): self.mapping=args['mapping']
  409.         for n in 'start', 'size', 'end':
  410.             if has_key(n): self.batch=1
  411.  
  412.         for n in 'orphan','overlap','previous','next':
  413.             if has_key(n) and not self.batch:
  414.                 raise ParseError, (
  415.                     """
  416.                     The %s attribute was used but neither of the
  417.                     <code>start</code>, <code>end</code>, or <code>size</code>
  418.                     attributes were used.
  419.                     """ % n, 'in')
  420.  
  421.         if has_key('start'):
  422.             v=args['start']
  423.             if type(v)==type(''):
  424.                 try: atoi(v)
  425.                 except:
  426.                     self.start_name_re=ts_regex.compile(
  427.                         '&+'+
  428.                         join(map(lambda c: "[%s]" % c, v),'')+
  429.                         '=[0-9]+&+')
  430.                     
  431.         name,expr=name_param(args,'in',1)
  432.         if expr is not None: expr=expr.eval
  433.         self.__name__, self.expr = name, expr
  434.         self.section=section.blocks
  435.         if len(blocks) > 1:
  436.             if len(blocks) != 2: raise ParseError, (
  437.                 'too many else blocks', 'in')
  438.             tname, args, section = blocks[1]
  439.             args=parse_params(args, name='')
  440.             if args:
  441.                 ename=name_param(args)
  442.                 if ename != name:
  443.                     raise ParseError, (
  444.                         'name in else does not match in', 'in')
  445.             self.elses=section.blocks
  446.             
  447.  
  448.     def renderwb(self, md):
  449.         expr=self.expr
  450.         name=self.__name__
  451.         if expr is None:
  452.             sequence=md[name]
  453.             cache={ name: sequence }
  454.         else:
  455.             sequence=expr(md)
  456.             cache=None
  457.  
  458.         if not sequence:
  459.             if self.elses: return render_blocks(self.elses, md)
  460.             return ''
  461.  
  462.         if type(sequence) is type(''):
  463.             raise 'InError', (
  464.                 'Strings are not allowed as input to the in tag.')
  465.  
  466.         section=self.section
  467.         params=self.args
  468.         
  469.         mapping=self.mapping
  470.  
  471.         if self.sort is not None:
  472.             sequence=self.sort_sequence(sequence)
  473.  
  474.         if self.reverse is not None:
  475.             sequence=self.reverse_sequence(sequence)
  476.             
  477.         next=previous=0
  478.         try: start=int_param(params,md,'start',0)
  479.         except: start=1
  480.         end=int_param(params,md,'end',0)
  481.         size=int_param(params,md,'size',0)
  482.         overlap=int_param(params,md,'overlap',0)
  483.         orphan=int_param(params,md,'orphan','3')
  484.         start,end,sz=opt(start,end,size,orphan,sequence)
  485.         if params.has_key('next'): next=1
  486.         if params.has_key('previous'): previous=1
  487.  
  488.         last=end-1
  489.         first=start-1
  490.  
  491.         try: query_string=md['QUERY_STRING']
  492.         except: query_string=''
  493.  
  494.         vars=sequence_variables(sequence,'?'+query_string,self.start_name_re)
  495.         kw=vars.data
  496.         kw['mapping']=mapping
  497.         kw['sequence-step-size']=sz
  498.         kw['sequence-step-overlap']=overlap
  499.         kw['sequence-step-start']=start
  500.         kw['sequence-step-end']=end
  501.         kw['sequence-step-start-index']=start-1
  502.         kw['sequence-step-end-index']=end-1
  503.         kw['sequence-step-orphan']=orphan
  504.  
  505.         push=md._push
  506.         pop=md._pop
  507.         render=render_blocks
  508.  
  509.         if cache: push(cache)
  510.         push(vars)
  511.         try:
  512.             if previous:
  513.                 if first > 0:
  514.                     pstart,pend,psize=opt(0,first+overlap,
  515.                                           sz,orphan,sequence)
  516.                     kw['previous-sequence']=1
  517.                     kw['previous-sequence-start-index']=pstart-1
  518.                     kw['previous-sequence-end-index']=pend-1
  519.                     kw['previous-sequence-size']=pend+1-pstart
  520.                     result=render(section,md)
  521.  
  522.                 elif self.elses: result=render(self.elses, md)
  523.                 else: result=''
  524.             elif next:
  525.                 try:
  526.                     # The following line is a sneaky way to test whether
  527.                     # there are more items, without actually
  528.                     # computing a length:
  529.                     sequence[end]
  530.                     pstart,pend,psize=opt(end+1-overlap,0,
  531.                                           sz,orphan,sequence)
  532.                     kw['next-sequence']=1
  533.                     kw['next-sequence-start-index']=pstart-1
  534.                     kw['next-sequence-end-index']=pend-1
  535.                     kw['next-sequence-size']=pend+1-pstart
  536.                     result=render(section,md)
  537.                 except:
  538.                     if self.elses: result=render(self.elses, md)
  539.                     else: result=''
  540.             else:
  541.                 result = []
  542.                 append=result.append
  543.                 validate=md.validate
  544.                 for index in range(first,end):
  545.                     if index==first and index > 0:
  546.                         pstart,pend,psize=opt(0,index+overlap,
  547.                                               sz,orphan,sequence)
  548.                         kw['previous-sequence']=1
  549.                         kw['previous-sequence-start-index']=pstart-1
  550.                         kw['previous-sequence-end-index']=pend-1
  551.                         kw['previous-sequence-size']=pend+1-pstart
  552.                     else:
  553.                         kw['previous-sequence']=0
  554.                         if index==last:
  555.                             try:
  556.                                 # The following line is a sneaky way to
  557.                                 # test whether there are more items,
  558.                                 # without actually computing a length:
  559.                                 sequence[end]
  560.                                 pstart,pend,psize=opt(end+1-overlap,0,
  561.                                                       sz,orphan,sequence)
  562.                                 kw['previous-sequence']=0
  563.                                 kw['next-sequence']=1
  564.                                 kw['next-sequence-start-index']=pstart-1
  565.                                 kw['next-sequence-end-index']=pend-1
  566.                                 kw['next-sequence-size']=pend+1-pstart
  567.                             except: pass
  568.         
  569.                     if index==last: kw['sequence-end']=1
  570.  
  571.                     client=sequence[index]
  572.  
  573.                     if validate is not None:
  574.                         try: vv=validate(sequence,sequence,None,client,md)
  575.                         except: vv=0
  576.                         if not vv:
  577.                             if (params.has_key('skip_unauthorized') and
  578.                                 params['skip_unauthorized']):
  579.                                 if index==first: kw['sequence-start']=0
  580.                                 continue
  581.                             raise ValidationError, index
  582.  
  583.                     kw['sequence-index']=index
  584.                     if type(client)==TupleType and len(client)==2:
  585.                         client=client[1]
  586.  
  587.                     if mapping: push(client)
  588.                     else: push(InstanceDict(client, md))
  589.  
  590.                     try: append(render(section, md))
  591.                     finally: pop(1)
  592.  
  593.                     if index==first: kw['sequence-start']=0
  594.  
  595.  
  596.                 result=join(result, '')
  597.  
  598.         finally:
  599.             if cache: pop()
  600.             pop()
  601.  
  602.         return result
  603.  
  604.     def renderwob(self, md):
  605.         """RENDER WithOutBatch"""
  606.         expr=self.expr
  607.         name=self.__name__
  608.         if expr is None:
  609.             sequence=md[name]
  610.             cache={ name: sequence }
  611.         else:
  612.             sequence=expr(md)
  613.             cache=None
  614.  
  615.         if not sequence:
  616.             if self.elses: return render_blocks(self.elses, md)
  617.             return ''
  618.  
  619.         if type(sequence) is type(''):
  620.             raise 'InError', (
  621.                 'Strings are not allowed as input to the in tag.')
  622.  
  623.         section=self.section        
  624.         mapping=self.mapping
  625.  
  626.         if self.sort is not None:
  627.             sequence=self.sort_sequence(sequence)
  628.  
  629.         if self.reverse is not None:
  630.             sequence=self.reverse_sequence(sequence)
  631.             
  632.         vars=sequence_variables(sequence)
  633.         kw=vars.data
  634.         kw['mapping']=mapping
  635.  
  636.         l=len(sequence)
  637.         last=l-1
  638.  
  639.         push=md._push
  640.         pop=md._pop
  641.         render=render_blocks
  642.  
  643.         if cache: push(cache)
  644.         push(vars)
  645.         try:
  646.                 result = []
  647.                 append=result.append
  648.                 validate=md.validate
  649.                 for index in range(l):
  650.                     if index==last: kw['sequence-end']=1
  651.                     client=sequence[index]
  652.  
  653.                     if validate is not None:
  654.                         try: vv=validate(sequence,sequence,None,client,md)
  655.                         except: vv=0
  656.                         if not vv:
  657.                             if (self.args.has_key('skip_unauthorized') and
  658.                                 self.args['skip_unauthorized']):
  659.                                 if index==1: kw['sequence-start']=0
  660.                                 continue
  661.                             raise ValidationError, index
  662.  
  663.                     kw['sequence-index']=index
  664.                     if type(client)==TupleType and len(client)==2:
  665.                         client=client[1]
  666.  
  667.                     if mapping: push(client)
  668.                     else: push(InstanceDict(client, md))
  669.  
  670.                     try: append(render(section, md))
  671.                     finally: pop()
  672.                     if index==0: kw['sequence-start']=0
  673.  
  674.                 result=join(result, '')
  675.  
  676.         finally:
  677.             if cache: pop()
  678.             pop()
  679.  
  680.         return result
  681.  
  682.     def sort_sequence(self, sequence):
  683.  
  684.         # Modified with multiple sort fields by Ross Lazarus
  685.         # April 7 2000 rossl@med.usyd.edu.au
  686.         # eg <dtml in "foo" sort=akey,anotherkey>
  687.         
  688.         sort=self.sort
  689.         sortfields = split(sort,',')   # multi sort = key1,key2 
  690.         multsort = len(sortfields) > 1 # flag: is multiple sort
  691.         mapping=self.mapping
  692.         isort=not sort
  693.         s=[]
  694.         for client in sequence:
  695.             k = None
  696.             if type(client)==TupleType and len(client)==2:
  697.                 if isort: k=client[0]
  698.                 v=client[1]
  699.             else:
  700.                 if isort: k=client
  701.                 v=client
  702.  
  703.             if sort:
  704.                  if multsort: # More than one sort key.
  705.                      k = []
  706.                      for sk in sortfields:
  707.                          try:
  708.                              if mapping: akey = v[sk]
  709.                              else: akey = getattr(v, sk)
  710.                          except AttributeError, KeyError: akey = None
  711.                          if not basic_type(akey):
  712.                              try: akey = akey()
  713.                              except: pass
  714.                          k.append(akey)
  715.                  else: # One sort key.
  716.                      try:
  717.                          if mapping: k = v[sort]
  718.                          else: k = getattr(v, sort)
  719.                      except AttributeError, KeyError: k = None
  720.                      if not basic_type(k):           
  721.                          try: k = k()
  722.                          except: pass
  723.  
  724.             s.append((k,client))
  725.  
  726.         s.sort()
  727.  
  728.         sequence=[]
  729.         for k, client in s:
  730.              sequence.append(client)
  731.         return sequence
  732.  
  733.     def reverse_sequence(self, sequence):
  734.         s=list(sequence)
  735.         s.reverse()
  736.         return s
  737.  
  738.  
  739. basic_type={type(''): 1, type(0): 1, type(0.0): 1, type(()): 1, type([]): 1,
  740.             type(None) : 1 }.has_key
  741.  
  742. def int_param(params,md,name,default=0, st=type('')):
  743.     try: v=params[name]
  744.     except: v=default
  745.     if v:
  746.         try: v=atoi(v)
  747.         except:
  748.             v=md[v]
  749.             if type(v) is st: v=atoi(v)
  750.     return v
  751.